/* =============================================================================
 * BeeWorks MoneyPile - Domain Model
 *
 * 
 * =============================================================================
 *
 * Developers: Jurgen Lust
 *
 * 
 * (C) Copyright 2009, BeeWorks BVBA.
 *
 */

package be.beeworks.moneypile.model;

import be.beeworks.moneypile.model.enums.DocumentType;

import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlRootElement;


/**
 * An invoice generating income for the company
 *
 * @author jlust
 * @author $LastChangedBy: Jurgen.Lust $
 *
 * @version $LastChangedRevision: 15 $
 */
@Entity
@Table(name = "INVOICES")
@Indexed
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Invoice
        extends AbstractDocument {
    //~ Static fields/initializers ---------------------------------------------

    private static final long serialVersionUID = 4722348966466708693L;

    //~ Instance fields --------------------------------------------------------

    @Temporal(TemporalType.DATE)
    private Date dueDate;
    @Temporal(TemporalType.DATE)
    private Date paidDate;
    @Temporal(TemporalType.DATE)
    private Date sentDate;
    @NotNull
    @Field(index = Index.TOKENIZED, store = Store.NO)
    @Column(unique = true)
    private Integer number;
    @NotNull
    @Min(0)
    @Max(100)
    private Integer percentVat;
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "invoice", cascade =  {
        CascadeType.ALL}
    )
    @XmlElement(name = "detail")
    private List<InvoiceDetail> details;
    @NotNull
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "project_id_ref", nullable = false)
    @XmlIDREF
    private Project project;

    //~ Constructors -----------------------------------------------------------

    public Invoice() {
        super();
        this.percentVat = 21;
        generateNumber();
        this.details = new ArrayList<InvoiceDetail>();
    }

    //~ Methods ----------------------------------------------------------------

    public Project getProject() {
        return project;
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public Date getSentDate() {
        return sentDate;
    }

    public void setSentDate(Date sentDate) {
        this.sentDate = sentDate;
    }

    public List<InvoiceDetail> getDetails() {
        return details;
    }

    public void setDetails(final List<InvoiceDetail> details) {
        if (details == null) {
            this.details.clear();
        } else {
            this.details = details;
        }
    }

    private void generateNumber() {
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(getCreationDate());

        StringBuilder numberBuilder = new StringBuilder();
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DATE);
        numberBuilder.append(year);

        if (month < 10) {
            numberBuilder.append("0");
        }

        numberBuilder.append(month);

        if (day < 10) {
            numberBuilder.append("0");
        }

        numberBuilder.append(day);
        numberBuilder.append(1);
        setNumber(Integer.valueOf(numberBuilder.toString()));
    }

    public Integer getNumber() {
        return number;
    }

    public void setNumber(final Integer number) {
        this.number = number;
    }

    public Date getPaidDate() {
        return paidDate;
    }

    public void setPaidDate(final Date paidDate) {
        this.paidDate = paidDate;
    }

    public Date getDueDate() {
        return dueDate;
    }

    public void setDueDate(final Date dueDate) {
        this.dueDate = dueDate;
    }

    public Integer getPercentVat() {
        return percentVat;
    }

    public void setPercentVat(final Integer percentVat) {
        this.percentVat = percentVat;
    }

    public Float getTotalPriceWithoutVat() {
        float totalPrice = 0f;

        for (InvoiceDetail invoiceDetail : getDetails()) {
            totalPrice += invoiceDetail.getPriceWithoutVat();
        }

        return totalPrice;
    }

    public Float getVatAmount() {
        if (getPercentVat() == null) {
            return 0f;
        }

        Float price = getTotalPriceWithoutVat();

        if (price == null) {
            return 0f;
        }

        return (price * getPercentVat()) / 100;
    }

    public Float getTotalPriceWithVat() {
        return getTotalPriceWithoutVat() + getVatAmount();
    }

    public Boolean isOpen() {
        return getSentDate() == null;
    }

    public Boolean isPaid() {
        return getPaidDate() == null;
    }

    public Boolean isWaitingForPayment() {
        return !isOpen() && !isPaid();
    }

    public Boolean isOverdue() {
        return isWaitingForPayment() && (getDueDate() != null) &&
        new Date().after(getDueDate());
    }

    @Override
    public DocumentType getType() {
        return DocumentType.Invoice;
    }
}
